
#include <sstream>
#include <sys/types.h>
#include <iostream>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
#include "Facade.h"
#include "Debug.h"
#include "Helper.h"

using namespace std;
Facade * Facade::_instance = 0;
//bool Facade:: m_bTimer = false;
//pthread_mutex_t Facade:: m_timerLock;

Facade::Facade() 
{
//	pthread_mutex_init(&m_timerLock, NULL);
//	m_iTimer = 0;
	main_base = NULL;
	DEBUG1(">>>>>>>>>>>>  create object Facade!");
}

Facade::~Facade()
{
	DEBUG1(">>>>>>>>>>>>  free object Facade now!");
}

Facade * Facade::Instance()
{
	if (NULL == _instance) 
	{
		_instance = new Facade();
	 	_instance->settings_init();
	}
	return _instance;
}

void Facade :: settings_init(void) 
{
	m_settings.access=0700;
	m_settings.port = 0;
	/* By default this string should be NULL for getaddrinfo() */
	m_settings.inter = NULL;
	m_settings.maxbytes = 64 * 1024 * 1024; /* default is 64MB */
	m_settings.maxconns = 1024;         /* to limit connections-related memory to about 5MB */
	m_settings.verbose = 0;
	m_settings.socketpath = NULL;      /* by default, not using a unix socket */
	m_settings.num_threads = 1;
}

void Facade :: Start( Handler *pF )
{
        m_handler = pF;
	/* initialize main thread libevent instance */
	main_base = event_init();
	/* initialize other stuff */
	m_network.conn_init();
	struct sigaction sa;
	sa.sa_handler = SIG_IGN;
	sa.sa_flags = 0;
	if (sigemptyset(&sa.sa_mask) == -1 || sigaction(SIGPIPE, &sa, 0) == -1) 
        {
		perror("failed to ignore SIGPIPE; sigaction");
		exit(EXIT_FAILURE);
	}
	/* start up worker threads if MT mode */
	m_threads.thread_init(m_settings.num_threads, main_base);
	/* create unix mode sockets after dropping privileges */
	if ( m_settings.socketpath != NULL) {
		if (m_network.server_socket_unix(m_settings.socketpath,m_settings.access)) 
                {
			fprintf(stderr, "failed to listen\n");
			exit(EXIT_FAILURE);
		}
	}

	/* create the listening socket, bind it, and init */
	if (m_settings.socketpath == NULL) {
                DEBUG1("server_socket");
		if ( m_network.server_socket( m_settings.port)) {
			fprintf(stderr, "failed to listen\n");
			exit(EXIT_FAILURE);
		}
	}

	//* Timer for worklist!
	Helper *p = Helper::Instance();

	if( p->getDefaultTimer() > 0)
	{        
		struct event wtimer;
		struct timeval tv;
		evtimer_set(&wtimer,Facade::Timer, &wtimer);
		evutil_timerclear(&tv);
		tv.tv_sec = p->getDefaultTimer();
		event_base_set(main_base, &wtimer);
		event_add(&wtimer, &tv);
	}

	/* enter the event loop */
	event_base_loop(main_base, 0);
	/* Clean up strdup() call for bind() address */
	if (m_settings.inter)
		free( m_settings.inter);
}

void Facade:: DoTimer()
{
//        INFO << "thread id = "<< pthread_self() << " : begin to process worklist!"<<endl;
        Helper *ph = Helper::Instance();
        long lt = time(NULL);
        if(ph != NULL)
                ph->Timer( lt );
//        SetTimerStatus(false);
}

/*
bool Facade :: GetTimerStatus()
{
        bool ret = false;
        pthread_mutex_lock(&m_timerLock);
        ret = m_bTimer;
        pthread_mutex_unlock(&m_timerLock);
        return ret;
}

bool Facade :: SetTimerStatus(bool status )
{
        pthread_mutex_lock(&m_timerLock);
        m_bTimer = status;
        pthread_mutex_unlock(&m_timerLock);
        return status;
}
*/

void Facade:: Timer(int fd, short event, void *arg)
{
        struct timeval tv;
	struct event *timeout = (struct event *)arg;
	Helper *p = Helper::Instance();
	int tvsec = p->getDefaultTimer();
	if(tvsec < 1)
		tvsec = 1;

	_instance->m_threads.dispatch_conn_new(0, 0, EV_READ | EV_PERSIST, DATA_BUFFER_SIZE, (char *) NANTX_EVENT_TIMER);
	/* 
        bool bstatus = GetTimerStatus();
        if(!bstatus)
        {
                SetTimerStatus(true);
                _instance->m_threads.dispatch_conn_new(0, 0, EV_READ | EV_PERSIST, DATA_BUFFER_SIZE, (char *) NANTX_EVENT_TIMER);
        }
        else
                INFO<<"one thread is doing NOW !!!!!!!! exit and check next time!"<<endl;
*/

        evutil_timerclear(&tv);
        tv.tv_sec = tvsec;
        event_add(timeout, &tv);
}

